<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>器灵</title>
</head>
<body onload="main()">
    <!-- <canvas id="canvasId" width="300" height="300"></canvas> -->
    <canvas id="canvasId" width="1024" height="512"></canvas>
    <canvas id="colorsCanvasId" width="256" height="1" style="width: 1024px;height: 16px;">粒子的颜色</canvas>
    <span>键盘上的左右箭头，wasd，空格，shift</span>
    <script src="./js/cuon-matrix.js"></script>

    <span>报错信息<button onclick="outputArea.value='';">清空</button></span>
    <textarea id="textareaId" cols="50" rows="5"></textarea>
    
<script>
const outputArea = document.getElementById("textareaId");
(function(){
    return function wrapFn (name){
        let oldFn = console[name];
        console[name] = (...args)=>{
            oldFn.apply(window, args);
            outputArea.value = `${outputArea.value}\n${new Date().toLocaleString()} ${JSON.stringify(args)}`;
        }
        return wrapFn;
    }
})()("log")("error")("warn");
// 获取粒子数组[64][64][64]
async function getPointsArray(x=0, y=0, z=0) {
    // const response = await fetch(`/points?x=${x}&y=${y}&z=${z}`);
    const response = await fetch(`http://192.168.137.1:7070/points?x=${x}&y=${y}&z=${z}`);
    const buffer = await response.arrayBuffer();
    // fetch(`http://192.168.137.1:7070/test`).then(x=>x.text()).then(x=>console.log(x));
    console.log(buffer.byteLength);
    return buffer;
}
// 粒子的颜色
const colorCtx = document.getElementById("colorsCanvasId").getContext("2d");
new Array(256).fill(0).map((_,i)=>{
    const num = Math.random(i)*0xFFFFFFFF;
    colorCtx.fillStyle = `rgba(${(num>>0)&0xFF},${(num>>8)&0xFF},${(num>>16)&0xFF},${((num>>16)&0xFF)/256.0})`;
    // colorCtx.fillStyle = 'rgba(0,200,200,1)';
    colorCtx.fillRect(i, 0, 1, 1);
    return colorCtx.fillStyle;
});
const 点的颜色数组 = colorCtx.getImageData(0,0,256,1);

// 画方块
const canvas    = document.getElementById("canvasId");
const gl        = canvas.getContext("webgl2");
const Utils= {}; // 记录一些帮助方法

// // 生成颜色的数组,js // new Array(256).fill(0).map((_,i)=>`colors[${i}]=vec4(${Math.random(1).toString().slice(0,5)}, 5.0, 6.0, 1.0);`).join("")
// const 顶点着色器程序 = `#version 300 es
// precision mediump float;precision highp int; // 精度
// in vec4 a_point;            // 点的位置 x,y,z, t
// in float a_pointSize;       // 点的大小
// // uniform vec4 colors[256];   // 点的颜色数组
// uniform sampler2D u_samplerColors;
// out vec4 fragColor;         // 点颜色
// void main() {
//     gl_Position = vec4(a_point.xyz / 64.0, 1.0);
//     fragColor = texture(u_samplerColors, vec2(0, a_point.w));
//     gl_PointSize = a_pointSize;
// }`;
// const 片元着色器程序 = `#version 300 es
// precision mediump float;precision highp int; // 精度
// in vec4 fragColor; // 颜色
// out vec4 FragColor;
// void main() {
//     FragColor = fragColor; 
// }`;

const 顶点着色器程序 = `#version 300 es
precision highp float; // 中等精度
in vec4 a_pointPosition; // 点的位置
in vec4 a_offset; // 偏移量
in vec4 a_normal; // 法线向量
in vec2 a_texCoord; // 纹理坐标
uniform mat4 u_mvpMatrix; // 模型视图投影矩阵
uniform mat4 u_normalMatrix; // 法线的矩阵（模型矩阵的逆转置矩阵）
uniform vec3 u_lightColor; // 光线颜色
uniform vec3 u_ambientLight; // 环境光颜色
uniform vec3 u_lightDirection; // 光线方向（归一化的世界坐标）
uniform sampler2D u_samplerColor; // 纹理color
out vec2 v_texCoord; // 纹理坐标（顶点着色器传给片元着色器）
out vec4 v_pointColor; // 点的颜色
out float nDotL; // 光线和法向量的点积
const vec3 p0 = vec3(128.0, 128.0, 128.0);
void main() {
    v_pointColor = texture(u_samplerColor, vec2(a_offset.w/256.0, 0));
    gl_Position = u_mvpMatrix * vec4((a_pointPosition.xyz + (a_offset.xzy-p0)), 1.0);
    vec3 normal = normalize(vec3(u_normalMatrix * a_normal)); // 对法向量进行归一化
    nDotL = max(dot(u_lightDirection, normal), 0.0); // 计算光线和法向量的点积
    v_texCoord = a_texCoord; // 传递给片元着色器
}`;
const 片元着色器程序 = `#version 300 es
precision highp float; // 中等精度
uniform vec3 u_lightColor; // 光线颜色
uniform vec3 u_ambientLight; // 环境光颜色
uniform sampler2D u_sampler0; // 纹理0
in vec2 v_texCoord; // 纹理坐标
in float nDotL; // 光线和法向量的点积
in vec4 v_pointColor; // 点的颜色
out vec4 FragColor; // 输出的颜色
void main() {
    vec4 color0 = texture(u_sampler0, v_texCoord);
    vec4 color = v_pointColor + (color0/256.0);
    // 计算漫反射的颜色
    vec3 diffuse = u_lightColor * vec3(color) * nDotL;
    // 环境光产生的反射光的颜色
    vec3 ambient = u_ambientLight * color.rgb;
    FragColor = vec4(diffuse+ambient+vec3(0.3,0.3,0.3), color.a+0.3);
}`;
function main(){
    try {
        _main();
    } catch (error) {
        console.error(error.stack);
        window.lastError = error;
        throw error;
    }
}
function _main() {
    gl.clearColor(1.0, 1.0, 0.0, 0.1);
    gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
    gl.enable(gl.DEPTH_TEST); // 启用深度测试
    gl.enable(gl.POLYGON_OFFSET_FILL); // 启用多边形偏移
    gl.polygonOffset(1.0, 1.0); // 指定偏移量的参数

    Utils.初始化着色器(gl, 顶点着色器程序, 片元着色器程序);

    // 获取变量的位置
    const 点的位置 = Utils.获取A变量位置(gl, 'a_pointPosition');
    const 偏移量位置 = Utils.获取A变量位置(gl, 'a_offset');
    const 纹理坐标 = Utils.获取A变量位置(gl, 'a_texCoord');
    const 变换矩阵 = Utils.获取U变量位置(gl, 'u_mvpMatrix');
    const 法线变换矩阵 = Utils.获取U变量位置(gl, 'u_normalMatrix');
    const 纹理0 = Utils.获取U变量位置(gl, 'u_sampler0');
    const 纹理1点的颜色 = Utils.获取U变量位置(gl, 'u_samplerColor');
    const 光线颜色 = Utils.获取U变量位置(gl, 'u_lightColor');
    const 光线方向 = Utils.获取U变量位置(gl, 'u_lightDirection');
    const 环境光颜色 = Utils.获取U变量位置(gl, 'u_ambientLight');
    
    // 设置模型矩阵
    const modelMatirx = new Matrix4(); // 模型矩阵
    // const currentAngle = 0.0;
    // modelMatirx.setRotate(currentAngle, 1, 0, 1);
    // modelMatirx.translate(-0.2, 0.0, 0.0);
    modelMatirx.setScale(1/16, 1/16, 1/16);
    // 设置视图矩阵
    const viewMatrix = new Matrix4(); // 视图矩阵
    viewMatrix.setLookAt(
        // 0.20, 0.25, 0.25, // 眼
        // 0.00, 0.00, 0.00, // 看
        // 0.00, 1.00, 0.00, // 头
        0, 0, 0, // 眼
        0, 0, -128, // 看
        0, 1, 0, // 头
    );
    // gl.uniformMatrix4fv(视图矩阵, false, viewMatrix.elements);
    // 设置投影矩阵
    const projMatrix = new Matrix4();
    // projMatrix.setOrtho(-1.0, 1.0, -1.0, 1.0, 0.0, 2.0); // 正交投影
    // projMatrix.setPerspective(30, canvas.width/canvas.height, 1, 100); // 透视投影
    projMatrix.setPerspective(45, canvas.width/canvas.height, 0.2, 100); // 透视投影
    // gl.uniformMatrix4fv(投影矩阵, false, projMatrix.elements);
    const mvpMatrix = new Matrix4(); // 模型视图投影矩阵
    mvpMatrix.set(projMatrix).multiply(viewMatrix).multiply(modelMatirx);
    gl.uniformMatrix4fv(变换矩阵, false, mvpMatrix.elements);
    const normalMatrix = new Matrix4(); // 法线的变换矩阵
    normalMatrix.setInverseOf(modelMatirx); // 逆矩阵
    normalMatrix.transpose(); // 转置
    gl.uniformMatrix4fv(法线变换矩阵, false, normalMatrix.elements);
    gl.uniform3f(光线颜色, 0.8, 0.8, 0.8); // 光线颜色
    const lightDirection = new Vector3([0.5, 3.0, 4.0]); // 光线方向（世界坐标系）
    lightDirection.normalize();
    gl.uniform3fv(光线方向, lightDirection.elements);
    gl.uniform3f(环境光颜色, 0.4, 0.5, 0.4);

    
    //    v6----- v5
    //   /|      /|
    //  v1------v0|
    //  | |     | |
    //  | |v7---|-|v4
    //  |/      |/
    //  v2------v3
    // 初始化缓冲区
    const 顶点们 = new Float32Array([
        // 立方体的顶点
        1.0, 1.0, 1.0,  -0.9, -0.9,  -1.0, 1.0, 1.0,  +0.5, +0.5,  -1.0,-1.0, 1.0, +0.3, +0.3,   1.0,-1.0, 1.0,  +0.0,+0.0, // v0-v1-v2-v3 front
        1.0, 1.0, 1.0,  -0.9, -0.9,   1.0,-1.0, 1.0,  +0.5, +0.5,   1.0,-1.0,-1.0, +0.3, +0.3,   1.0, 1.0,-1.0,  +0.0,+0.0, // v0-v3-v4-v5 right
        1.0, 1.0, 1.0,  -0.9, -0.9,   1.0, 1.0,-1.0,  +0.5, +0.5,  -1.0, 1.0,-1.0, +0.3, +0.3,  -1.0, 1.0, 1.0,  +0.0,+0.0, // v0-v5-v6-v1 up
        -1.0, 1.0, 1.0, -0.9, -0.9,  -1.0, 1.0,-1.0,  +0.5, +0.5,  -1.0,-1.0,-1.0, +0.3, +0.3,  -1.0,-1.0, 1.0,  +0.0,+0.0, // v1-v6-v7-v2 left
        -1.0,-1.0,-1.0, -0.9, -0.9,   1.0,-1.0,-1.0,  +0.5, +0.5,   1.0,-1.0, 1.0, +0.3, +0.3,  -1.0,-1.0, 1.0,  +0.0,+0.0, // v7-v4-v3-v2 down
        1.0,-1.0,-1.0,  -0.9, -0.9,  -1.0,-1.0,-1.0,  +0.5, +0.5,  -1.0, 1.0,-1.0, +0.3, +0.3,   1.0, 1.0,-1.0,  +0.0,+0.0, // v4-v7-v6-v5 back
    ]);
    const pointLen = 8; // 点的个数
    const FLOAT_SIZE = 顶点们.BYTES_PER_ELEMENT; // float 数据的大小
    const BYTE_SIZE = 8;
    const 顶点索引们 = new Uint8Array([
        0, 1, 2,   0, 2, 3,    // front
        4, 5, 6,   4, 6, 7,    // right
        8, 9,10,   8,10,11,    // up
        12,13,14,  12,14,15,    // left
        16,17,18,  16,18,19,    // down
        20,21,22,  20,22,23     // back
    ]);
    const indexLen = 36;
    var 偏移量们 = new Float32Array([
        56, 56, 56, 56,
        128, 128, 128, 128
    ]);
    var 点的个数 = 偏移量们.length / 4;
    
    // ##########  初始化缓冲区  ##########
    // 1. 创建缓冲区对象
    const 顶点缓冲区 = gl.createBuffer();
    const 偏移量缓冲区 = gl.createBuffer();
    const 纹理缓冲区 = gl.createBuffer();
    const 索引缓冲区 = gl.createBuffer();
    if ( ! 顶点缓冲区 || ! 偏移量缓冲区 || ! 纹理缓冲区 || ! 索引缓冲区)console.error("缓冲区创建失败");

    // 2. 将数据写入缓冲区对象并开启
    // 3. 分配缓冲区并开启
    gl.bindBuffer(gl.ARRAY_BUFFER, 顶点缓冲区);
    gl.bufferData(gl.ARRAY_BUFFER, 顶点们, gl.STATIC_DRAW);
    gl.vertexAttribPointer(点的位置, 3, gl.FLOAT, false, FLOAT_SIZE * 5, 0); 
    gl.enableVertexAttribArray(点的位置);
    
    gl.bindBuffer(gl.ARRAY_BUFFER, 偏移量缓冲区);
    gl.bufferData(gl.ARRAY_BUFFER, 偏移量们, gl.STATIC_DRAW);
    gl.vertexAttribPointer(偏移量位置, 4, gl.FLOAT, false, FLOAT_SIZE * 4, 0); 
    gl.enableVertexAttribArray(偏移量位置);
    gl.vertexAttribDivisor(偏移量位置, 1); // 1 表示每一个立方体的绘制期间，该变量保持不变


    gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, 索引缓冲区);
    gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, 顶点索引们, gl.STATIC_DRAW);
    gl.vertexAttribPointer(纹理坐标, 2, gl.FLOAT, false, FLOAT_SIZE * 5, FLOAT_SIZE * 3);
    gl.enableVertexAttribArray(纹理坐标);

    // 法线
    const normals = new Float32Array([
        0.0, 0.0, 1.0,   0.0, 0.0, 1.0,   0.0, 0.0, 1.0,   0.0, 0.0, 1.0,  // v0-v1-v2-v3 front
        1.0, 0.0, 0.0,   1.0, 0.0, 0.0,   1.0, 0.0, 0.0,   1.0, 0.0, 0.0,  // v0-v3-v4-v5 right
        0.0, 1.0, 0.0,   0.0, 1.0, 0.0,   0.0, 1.0, 0.0,   0.0, 1.0, 0.0,  // v0-v5-v6-v1 up
        -1.0, 0.0, 0.0,  -1.0, 0.0, 0.0,  -1.0, 0.0, 0.0,  -1.0, 0.0, 0.0,  // v1-v6-v7-v2 left
        0.0,-1.0, 0.0,   0.0,-1.0, 0.0,   0.0,-1.0, 0.0,   0.0,-1.0, 0.0,  // v7-v4-v3-v2 down
        0.0, 0.0,-1.0,   0.0, 0.0,-1.0,   0.0, 0.0,-1.0,   0.0, 0.0,-1.0   // v4-v7-v6-v5 back
    ]);
    Utils.设置缓冲区的值(gl, "a_normal", normals, 3, gl.FLOAT);


    // #########  点的信息 ###############
    var pointX=0, pointY=0, pointZ=128;
    function flushPoints(){
        getPointsArray(pointX, pointY, pointZ).then(arrayBuffer => {
            // 点的信息
            gl.bindBuffer(gl.ARRAY_BUFFER, 偏移量缓冲区);
            gl.bufferData(gl.ARRAY_BUFFER, arrayBuffer, gl.STATIC_DRAW);
            // gl.bufferData(gl.ARRAY_BUFFER, arrayBuffer, gl.DYNAMIC_DRAW);
            gl.vertexAttribPointer(偏移量位置, 4, gl.UNSIGNED_BYTE, false, BYTE_SIZE * 4, 0); 
            gl.enableVertexAttribArray(偏移量位置);
            gl.vertexAttribDivisor(偏移量位置, 1); // 1 表示每一个立方体的绘制期间，该变量保持不变

            // 绘制
            gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
            点的个数=arrayBuffer.byteLength/4;
            // gl.drawArrays(gl.POINTS, 0, 点的个数); // 三个点
            // gl.drawArrays(gl.TRIANGLES, 0, 点的个数); // 三角形
            // gl.drawArrays(gl.LINES, 0, 点的个数); // 线
            gl.drawElementsInstanced(gl.TRIANGLES, indexLen, gl.UNSIGNED_BYTE, 0, 点的个数);
        }).finally(()=>{
            setTimeout(flushPoints, 1000);
        });
    }
    flushPoints();


    // ##########  配置和加载纹理  ##########

    // Promise.all([
    //     // 图片 sky
    //     new Promise((resolve, reject)=>{
    //         const 图片 = new Image();
    //         图片.onload = ()=>resolve(图片);
    //         图片.src = "./resources/sky.jpg";
    //     }).then(图片=>{
    //         加载纹理(gl, 图片, 纹理0, 0);
    //     })
    // ]).then(() => {
    //     // 加载完毕，画图
    //     gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
    //     // gl.drawArrays(gl.TRIANGLE_STRIP, 0, n);
    //     // gl.drawArrays(gl.TRIANGLES, 0, pointLen);
    //     gl.drawElements(gl.TRIANGLES, indexLen, gl.UNSIGNED_BYTE, 0);
    // });
    加载纹理(gl, 点的颜色数组, 纹理0, 0);
    加载纹理(gl, 点的颜色数组, 纹理1点的颜色, 0);
    
    // 注册键盘事件
    let eye = {x:0.20, y:0.25, z:0.25};
    let rotate1 = 0.0; // 旋转角度1
    let rotate2 = 0.0; // 旋转角度2
    let step1 = 0.0; // 脚丫子的位置
    let step2 = 0.0; // 脚丫子的位置
    let step3 = 0.0; // 脚丫子的位置
    let rotateAdd = 1; // 每次加多少
    // let stepAdd = 0.1; // 每次加多少
    function stepAdd(vec3){ 
        step1+=vec3.elements[0]/16; 
        // step2+=vec3.elements[1]; 
        step3+=vec3.elements[2]/16; 
    }
    document.onkeydown = outputArea.onkeydown = function(e){
        console.debug(`e.keyCode=${e.keyCode},rotate1=${rotate1},rotate2=${rotate2},step1=${step1},step2=${step2},step3=${step3}`);
        if(e.keyCode == 39 || e.keyCode==76){ // 按下右键
            rotate1 = (rotate1-rotateAdd)%360;
            // rotate1 = -rotateAdd;
        } else if(e.keyCode == 37 || e.keyCode==74){ // 按下左键
            rotate1 = (rotate1+rotateAdd)%360;
            // rotate1 = rotateAdd;
        } else if((e.keyCode == 38 || e.keyCode==73) && rotate2-rotateAdd > -90){ // 上
            rotate2 = (rotate2-rotateAdd)%90;
            // rotate2 = -rotateAdd;
        } else if((e.keyCode == 40 || e.keyCode==75) && rotate2+rotateAdd < 90) { // 下
            rotate2 = (rotate2+rotateAdd)%90;
            // rotate2 = rotateAdd;
        }
        // modelMatirx.rotate(rotate1, 0, 1, 0);
        // modelMatirx.rotate(rotate2, 1, 0, 0);
        // viewMatrix.rotate(rotate1, 0, 1, 0);
        // viewMatrix.rotate(rotate2, 1, 0, 0);
        const newViewMatrix = new Matrix4(); // 视图矩阵
        newViewMatrix.rotate(rotate1, 0, 1, 0);
        newViewMatrix.rotate(rotate2, 1, 0, 0);
        const vec3 = new Vector3([0,0,1]);
        const newVec3 = newViewMatrix.multiplyVector3(vec3).normalize();
        newViewMatrix.setLookAt(
            0, 0, 0, // 眼
            newVec3.elements[0], newVec3.elements[1], newVec3.elements[2], // 看
            0, 1, 0, // 头
        );
        

        const stepAddVec3 = new Vector3([newVec3.elements[0], 0, newVec3.elements[2]]).normalize();
        if(e.keyCode == 87) { // W 
            stepAdd(new Vector3([-newVec3.elements[0],  0, -newVec3.elements[2]]).normalize());
            // step3 -= stepAdd;
        } else if(e.keyCode == 83) { // S
            stepAdd(stepAddVec3);
            // step3 += stepAdd;
        } else if(e.keyCode == 65) { // A
            stepAdd(new Matrix4().rotate(-90, 0,1,0).multiplyVector3(stepAddVec3).normalize());
            // step1 -= stepAdd;
        } else if(e.keyCode == 68) { // D
            stepAdd(new Matrix4().rotate(90, 0,1,0).multiplyVector3(stepAddVec3).normalize());
            // step1 += stepAdd;
        } else if(e.keyCode == 16) { // SHIFT
            step2 += 1/32;
        } else if(e.keyCode == 32) { // SPACE
            step2 -= 1/32;
        } else {
            console.log(e.keyCode);
            // return; // 不管其他的
        }
        newViewMatrix.translate(step1, step2, step3);

        // gl.uniformMatrix4fv(模型矩阵, false, modelMatirx.elements);
        mvpMatrix.set(projMatrix).multiply(newViewMatrix).multiply(modelMatirx);
        gl.uniformMatrix4fv(变换矩阵, false, mvpMatrix.elements);
        normalMatrix.setInverseOf(modelMatirx); // 逆矩阵
        normalMatrix.transpose(); // 转置
        gl.uniformMatrix4fv(法线变换矩阵, false, normalMatrix.elements);
        gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
        // gl.drawArrays(gl.TRIANGLES, 0, pointLen);
        // gl.drawElements(gl.TRIANGLES, indexLen, gl.UNSIGNED_BYTE, 0);
        gl.drawElementsInstanced(gl.TRIANGLES, indexLen, gl.UNSIGNED_BYTE, 0, 点的个数);

        // 阻止输入
        return false;
    }
}

// 加载图片
function 加载纹理(gl, 图片, 纹理, 纹理单元编号){

    // 创建纹理对象
    const 纹理对象 = gl.createTexture();
    if ( ! 纹理对象 )console.error("纹理对象创建失败");

    // 开启 n 号纹理单元
    gl.activeTexture(gl[`TEXTURE${纹理单元编号}`]);
    // 对纹理图像进行 y 轴反转
    gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, 1);
    // 向 target 绑定纹理对象
    gl.bindTexture(gl.TEXTURE_2D, 纹理对象);
    
    // 配置纹理参数
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); // 缩小时，线性采样
    // gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); // 水平延伸时，拉伸边缘
    // gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.MIRRORED_REPEAT); // 竖直延伸时，镜像
    // 配置纹理图像
    gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, 图片);

    // 将纹理的编号 n 传递给着色器
    gl.uniform1i(纹理, 纹理单元编号);

}

// ########## 其他 ##########

/**
 * 建立并初始化着色器
 */
Utils.初始化着色器 = function(gl, 顶点代码, 片元代码) {
    
    const 顶点着色器 = gl.createShader(gl.VERTEX_SHADER);
    gl.shaderSource(顶点着色器, 顶点代码);
    gl.compileShader(顶点着色器);
    if( ! gl.getShaderParameter(顶点着色器, gl.COMPILE_STATUS)){
        console.error("错误：顶点着色器编译失败");
        console.error(gl.getShaderInfoLog(顶点着色器));
        return;
    }
    gl.顶点着色器 = 顶点着色器;

    const 片元着色器 = gl.createShader(gl.FRAGMENT_SHADER);
    gl.shaderSource(片元着色器, 片元代码);
    gl.compileShader(片元着色器);
    if( ! gl.getShaderParameter(片元着色器, gl.COMPILE_STATUS)){
        console.error("错误：片元着色器编译失败");
        console.error(gl.getShaderInfoLog(片元着色器));
        return;
    }
    gl.片元着色器 = 片元着色器;

    const 着色器程序 = gl.createProgram();
    gl.attachShader(着色器程序, 顶点着色器);
    gl.attachShader(着色器程序, 片元着色器);
    gl.linkProgram(着色器程序);
    if( ! gl.getProgramParameter(着色器程序, gl.LINK_STATUS)){
        console.error("错误：链接着色器程序失败");
        console.error(gl.getProgramInfoLog(着色器程序));
        return;
    }
    gl.useProgram(着色器程序);
    gl.着色器程序 = 着色器程序;
}

/**
 * 获取变量位置
 */
Utils.获取A变量位置 = function(gl, 变量名) {
    let 变量位置 = gl.getAttribLocation(gl.着色器程序, 变量名);
    if(变量位置 < 0) {
        console.error(`错误：获取变量(${变量名})的位置失败`);
    }
    return 变量位置;
}
Utils.获取U变量位置 = function(gl, 变量名) {
    let 变量位置 = gl.getUniformLocation(gl.着色器程序, 变量名);
    if( ! 变量位置) {
        console.error(`错误：获取变量(${变量名})的位置失败`);
    }
    return 变量位置;
}
Utils.设置缓冲区的值 = function(gl, 变量名, data, num, type) {
    // 创建缓冲区对象
    var buffer = gl.createBuffer();
    if (!buffer) {
        console.log('创建缓冲区对象失败');
        return false;
    }
    // 将数据写入缓冲区
    gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
    gl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW);
    // 绑定变量
    var a_attribute = gl.getAttribLocation(gl.着色器程序, 变量名);
    if (a_attribute < 0) {
        console.log('获取变量位置失败：' + attribute);
        return false;
    }
    gl.vertexAttribPointer(a_attribute, num, type, false, 0, 0);
    // 启用缓冲区
    gl.enableVertexAttribArray(a_attribute);

    return true;
}

















































</script>
</body>
</html>